﻿using System;
using System.Net;
using System.Net.Sockets;
using VincentUtil.Ntp;


namespace TimeSyncServer
{

    public class NtpServer
    {
        private UdpClient _udpClient;
        public delegate void MessageShowHandler(string str);
        public delegate void NtpReceiveHandler(NtpReceiveInfo receiveInfo);
        /// <summary>
        /// 更新日志事件
        /// </summary>
        public event MessageShowHandler LogUpdateInfo;
        public event MessageShowHandler LogUpdateError;
        public event MessageShowHandler LogUpdateDebug;
        // 更新列表
        public event NtpReceiveHandler NtpReceiveInfoUpdate;

        public string NtpServerAddress { get; }
        public int NtpServerPort { get; }

        private bool _isOpen;

        public NtpServer(string ntpServerAddress, int ntpServerPort)
        {
            NtpServerAddress = ntpServerAddress;
            NtpServerPort = ntpServerPort;
        }

        public void Start()
        {
            _isOpen = true;
            IPAddress ntpAddress = IPAddress.Parse(NtpServerAddress);
            _udpClient = new UdpClient(new IPEndPoint(ntpAddress, NtpServerPort));
            LogUpdateInfo?.Invoke("已建立UDP监听...");

            while (_udpClient != null && _isOpen)
            {
                try
                {
                    IPEndPoint remoteEP = null;
                    byte[] receivedBytes = _udpClient.Receive(ref remoteEP);
                    DoNtpResponse(remoteEP, receivedBytes);
                }
                catch (Exception ex)
                {
                    LogUpdateError?.Invoke($"程序异常：{ex.Message}-{ex.StackTrace}");
                    continue;
                }
            }

        }

        private void DoNtpResponse(IPEndPoint remoteEP, byte[] receivedBytes)
        {
            NtpReceiveInfo ntpReceive = new NtpReceiveInfo(remoteEP.Address.ToString());
            try
            {
                NtpResponseMessage res = new NtpResponseMessage();
                res.ReceiveTime = DateTime.UtcNow;

                if (!NtpSendMessage.TryResolve(receivedBytes, out var message))
                {
                    ntpReceive.CurrentState = "格式校验失败";
                    return;
                }

                res.OriginTime = message.OriginTime;
                res.TransmitTime = DateTime.UtcNow;
                ntpReceive.OriginTime.Set(message.OriginTime);
                ntpReceive.ResponseTime.Set(res.TransmitTime);
                ntpReceive.CurrentState = "正在响应...";
   
                var responseBytes = res.BuildNTPBytes();

                _udpClient.Send(responseBytes, responseBytes.Length, remoteEP);

                ntpReceive.CurrentState = $"已校准时差{ntpReceive.GetCalibratedValue()}ms";
            }
            catch (Exception ex)
            {
                ntpReceive.CurrentState = ex.Message;
                LogUpdateError?.Invoke($"响应异常：{ex.Message}-{ex.StackTrace}");
            }
            finally
            {
                NtpReceiveInfoUpdate?.Invoke(ntpReceive);
            }
        }

        private bool IsValid(byte[] receivedBytes)
        {
            if (receivedBytes.Length != 48)
            {
                LogUpdateDebug?.Invoke("字符串长度不为48");
                return false;
            }
            if (receivedBytes[0] != 0x1B)
            {
                LogUpdateDebug?.Invoke($"关键字节：{receivedBytes[0]}，应为0x1B");
                return false;
            }
            return true;
        }
        public void Stop()
        {
            if (_udpClient != null)
                _udpClient.Close();
            _isOpen = false;
        }

    }

    public class NtpReceiveInfo
    {
        public string RequestAddress;
        public string CurrentState;
        public Optional<DateTime> OriginTime = new Optional<DateTime>();
        public Optional<DateTime> ResponseTime = new Optional<DateTime>();

        public NtpReceiveInfo(string requestAddress)
        {
            RequestAddress = requestAddress;
            CurrentState = "";
        }

        /// <summary>
        /// 获取误差(ms)
        /// </summary>
        /// <returns></returns>
        public double GetCalibratedValue()
        {
            if (OriginTime.TryGet(out DateTime originTime) && ResponseTime.TryGet(out DateTime responseTime))
            {
                return Math.Round(originTime.Subtract(responseTime).TotalMilliseconds, 1);
            }
            return 0;
        }
    }

    /// <summary>
    /// 可为空的struct
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Optional<T> where T : struct
    {
        private T _value;
        bool _hasValue;

        public Optional()
        {
            _value = default(T);
            _hasValue = false;
        }

        public void Set(T value)
        {
            _value = value;
            _hasValue = true;
        }

        public bool TryGet(out T value)
        {
            if (_hasValue)
            {
                value = _value;
                return true;
            }
            else
            {
                value = default(T);
                return false;
            }
        }

    }
}
